// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
// http://www.zhengzhong.net/
//
// $Id: transformer.cpp 318 2009-03-02 14:03:21Z heavyzheng $
//

#ifndef ZZHENG_XML_SOURCE
#define ZZHENG_XML_SOURCE
#endif // ZZHENG_XML_SOURCE

#include <zzheng/xml/xslt/libxslt/transformer.hpp>
#include <zzheng/xml/xslt/libxslt/xslt_result.hpp>
#include <zzheng/xml/xslt/exception.hpp>

#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

#include <boost/shared_ptr.hpp>
#include <cassert>
#include <cstring>
#include <functional>
#include <string>


namespace zzheng {
namespace xml {
namespace xslt {
namespace libxslt_ {

    namespace {

        //! Function object for deleting an xsltStylesheet pointer. This function object is used
        //! as the deleter parameter of boost::shared_ptr<xsltStylesheet>.
        struct delete_xslt_stylesheet: public std::unary_function<xsltStylesheet*, void> {
            void operator()(xsltStylesheet* stylesheet) const {
                if (stylesheet != 0) {
                    xsltFreeStylesheet(stylesheet);
                }
            }
        }; // struct delete_xslt_stylesheet

    } // anonymous namespace

    ////////////////////////////////////////////////////////////////////////////////////////////////

    transformer::transformer(): stylesheet_() {
        // Do nothing.
    }

    bool transformer::loaded() const {
        return (stylesheet_ != 0);
    }

    void transformer::load_from_file(const std::string& file_name) {
        xmlDoc* doc = xmlParseFile(file_name.c_str());
        if (doc != 0) {
            if (!load_from_xmldoc(doc)) {
                xmlFreeDoc(doc);
            }
        }
        if (!loaded()) {
            std::string what = "Failed to load XSLT stylesheet from file '" + file_name + "'.";
            throw xslt_error(what.c_str());
        }
    }

    void transformer::load_from_string(char const* str) {
        int size = static_cast<int>(std::strlen(str));
        xmlDoc* doc = xmlParseMemory(str, size);
        if (doc != 0) {
            if (!load_from_xmldoc(doc)) {
                xmlFreeDoc(doc);
            }
        }
        if (!loaded()) {
            throw xslt_error("Failed to load XSLT stylesheet from string.");
        }
    }

    bool transformer::load_from_xmldoc(xmlDoc* doc) {
        assert(doc != 0 && "load_from_xmldoc() called with null libxml2 document.");
        stylesheet_.reset();
        xsltStylesheet* raw = xsltParseStylesheetDoc(doc);
        if (raw != 0) {
            stylesheet_.reset(raw, delete_xslt_stylesheet());
            return true;
        } else {
            return false;
        }
    }

    xslt_result transformer::transform_file(const std::string& file_name) {
        // Parse the source XML file to an xmlDoc object.
        xmlDoc* doc = xmlParseFile(file_name.c_str());
        if (doc == 0) {
            std::string what = "Failed to parse XML file '" + file_name + "' to transform.";
            throw xslt_error(what.c_str());
        }
        // Apply the XSLT stylesheet to the source document, and free the source document.
        xslt_result result = transform_xmldoc(doc);
        xmlFreeDoc(doc);
        // Return the transformed result, or throw an error if result is not good.
        if (result.good()) {
            return result;
        } else {
            std::string what = "Failed to apply stylesheet to XML file '" + file_name + "'.";
            throw xslt_error(what.c_str());
        }
    }

    xslt_result transformer::transform_string(const char* str) {
        // Parse the source XML string to an xmlDoc object.
        int size = static_cast<int>(std::strlen(str));
        xmlDoc* doc = xmlParseMemory(str, size);
        if (doc == 0) {
            throw xslt_error("Failed to parse XML string for transform.");
        }
        // Apply the XSLT stylesheet to the source document, and free the source document.
        xslt_result result = transform_xmldoc(doc);
        xmlFreeDoc(doc);
        // Return the transformed result, or throw an error if result is not good.
        if (result.good()) {
            return result;
        } else {
            throw xslt_error("Failed to apply stylesheet to XML string.");
        }
    }

    xslt_result transformer::transform_xmldoc(xmlDoc* doc) {
        assert(doc != 0 && "transform_xmldoc() called with null libxml2 document.");
        xmlDoc* result_doc = 0;
        if (stylesheet_ != 0) {
            // TODO: Prepare XSLT transformation parameters.
            const char* xslt_params[16 + 1];
            xslt_params[0] = 0;
            // Apply the XSLT stylesheet to the source XML document.
            result_doc = xsltApplyStylesheet(stylesheet_.get(), doc, xslt_params);
        }
        return xslt_result(result_doc, stylesheet_);
    }


} // namespace zzheng::xml::xslt::libxslt_
} // namespace zzheng::xml::xslt
} // namespace zzheng::xml
} // namespace zzheng



